using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using ProjectEuler.Core.Helpers;

namespace ProjectEuler.Core
{
    public class Problem68 : IProjectEulerProblem
    {
        public int Number
        {
            get { return 68; }
        }

        public string Description
        {
            get { return "What is the maximum 16-digit string for a \"magic\" 5-gon ring?"; }
        }

        public string Answer
        {
            get { return GetMagic5GonSolutions().Where(gon => gon.Length == 16).Max(); }
        }

        protected IEnumerable<string> GetMagic5GonSolutions()
        {
            var perms = GetAllPermutations();
            return perms.Select(p => new Magic5Gon(p))
                        .Where(g => g.IsMagic())
                        .Select(g => g.UniqueSolutionSet());
        }

        protected IEnumerable<IEnumerable<int>> GetAllPermutations()
        {
            var p = new StringPerm("1,2,3,4,5,6,7,8,9,10".Split(','));
            var perms = p.EnumerateAllPermuations();
            return perms.Select(p1 => p1.Select(p2 => int.Parse(p2)));
        }

        public class Magic5Gon
        {
            private readonly IList<int> _numbers;

            private readonly IList<IList<int>> _indexSets = new List<IList<int>>
                                                       {
                                                           new List<int> {0, 1, 2},
                                                           new List<int> {3, 2, 4},
                                                           new List<int> {5, 4, 7},
                                                           new List<int> {6, 7, 8},
                                                           new List<int> {9, 8, 1}
                                                       };

            public Magic5Gon(IEnumerable<int> numbers)
            {
                _numbers = numbers.ToList();
            }

            public bool IsMagic()
            {
                var sum = Set(0).Sum();
                for (int i = 1; i < 5; i++)
                {
                    if (Set(i).Sum() != sum) return false;
                }
                return true;
            }

            public string UniqueSolutionSet()
            {
                var startIndex = GetStartingIndexSet();
                var solution = "";
                for (int i = 0; i < 5; i++)
                {
                    solution +=  _numbers[_indexSets[startIndex][0]] + "" +
                               _numbers[_indexSets[startIndex][1]] + "" +
                               _numbers[_indexSets[startIndex][2]];
                    startIndex++; if (startIndex == 5) startIndex = 0;
                }
                return solution;
            }

            private int GetStartingIndexSet()
            {
                var lowest = _indexSets.Min(i => _numbers[i[0]]);
                var set = _indexSets.First(i => _numbers[i[0]] == lowest);
                return _indexSets.IndexOf(set);
            }

            private IEnumerable<int> Set(int i)
            {
                var indexSet = _indexSets[i];
                return new List<int> { _numbers[indexSet[0]], _numbers[indexSet[1]], _numbers[indexSet[2]] };
            }
        }
    }

    [TestFixture]
    public class Problem68Tests : Problem68
    {
        [Test]
        public void Can_get_unique_solution_string()
        {
            var g = new Magic5Gon(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
            Assert.That(g.UniqueSolutionSet(), Is.EqualTo("1234356587891092"));
        }
    }
}